package tcp

import (
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"lib/network"
	"lib/structs/ams"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type ConnStruct struct {
	conn        net.Conn      // 当前连接
	connMutex   sync.RWMutex  // 连接读写锁
	reconnectCh chan struct{} // 触发重连的信号
	quitCh      chan struct{} // 退出信号
	connected   atomic.Bool

	adds       []string
	backoff    time.Duration // 连接建立失败重连间隔
	backoffMax time.Duration // 连接建立失败重连最大间隔
}

func NewTCPClient(backoff, backoffMax int64, adds []string, tryCh chan string) *ConnStruct {
	c := &ConnStruct{
		reconnectCh: make(chan struct{}),
		quitCh:      make(chan struct{}),
		backoff:     time.Duration(backoff) * time.Second,
		backoffMax:  time.Duration(backoffMax) * time.Second,
		adds:        adds,
	}
	// 初始连接
	go c.tryConnect(c.adds, tryCh)
	go c.connectionManager(c.adds, tryCh)
	return c
}

// 连接管理协程 (核心重连逻辑)
func (c *ConnStruct) connectionManager(adds []string, tryCh chan string) {
	for {
		select {
		case <-c.reconnectCh:
			// 收到重连信号
			c.tryConnect(adds, tryCh)
		}
	}
}

// 尝试建立连接 (带指数退避重试)
func (c *ConnStruct) tryConnect(adds []string, tryCh chan string) {
	if c.connected.Load() {
		return
	}
	backoff := c.backoff
	maxBackoff := c.backoffMax

	for {
		for _, item := range adds {
			dialer := &net.Dialer{
				Timeout: time.Duration(ams.TcpDeadline) * time.Second,
			}
			c.connMutex.Lock()
			if c.connected.Load() {
				c.connMutex.Unlock()
				return
			}
			logrus.Debugf("tryConnect add:%s ", item)
			conn, err := dialer.Dial("tcp", item)
			if err == nil {
				if c.conn != nil {
					_ = c.conn.Close()
				}
				c.conn = conn
				c.connected.Store(true)
				c.connMutex.Unlock()
				tryCh <- item
				return
			}
			c.connMutex.Unlock()
		}
		time.Sleep(backoff)
		// 指数退避
		backoff *= 2
		if backoff > maxBackoff {
			backoff = maxBackoff
		}
	}
}

// 读取服务器数据
func (c *ConnStruct) Read() (bs []byte, err error) {
	if !c.connected.Load() {
		c.scheduleReconnect()
		err = errors.New("not connected")
		return
	}
	if c.conn == nil {
		c.scheduleReconnect()
		err = errors.New("connection not established")
		return
	}
	bs, err = network.Decode(c.conn)
	if err != nil {
		c.scheduleReconnect()
		err = fmt.Errorf("read err[%w]", err)
		return
	}
	return
}

// Send 发送数据 (线程安全)
func (c *ConnStruct) Send(bs []byte) error {
	if !c.connected.Load() {
		c.scheduleReconnect()
		return errors.New("not connected")
	}
	if c.conn == nil {
		c.scheduleReconnect()
		return errors.New("connection not established")
	}
	_, err := c.conn.Write(bs)
	if err != nil {
		c.scheduleReconnect()
		return fmt.Errorf("send failed[%w]", err)
	}
	return nil
}

// 触发重连 (非阻塞)
func (c *ConnStruct) scheduleReconnect() {
	c.connected.Store(false)
	select {
	case c.reconnectCh <- struct{}{}:
		// 成功发送重连信号
	default:
		// 已经有未处理的信号
	}
}

// IsConnected 是否连接成功
func (c *ConnStruct) IsConnected() bool {
	return c.connected.Load()
}
